package core;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * 用例模块, 通过给定的参数表创建 Fitnesse 中使用的测试脚本
 * @author Bob
 *
 */
public class CaseModule {
	private boolean flag = false;
	public boolean isFlag() {
		return flag;
	}
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	public List<ApiClass> getApiList() {
		return apiList;
	}
	public void setApiList(List<ApiClass> apiList) {
		this.apiList = (ArrayList<ApiClass>) apiList;
	}

	public StringBuilder getSb() {
		return sb;
	}
	public void setSb(StringBuilder sb) {
		this.sb = sb;
	}
	//保存接口信息和参数对照表
	private ArrayList<ApiClass> apiList = null;
	//录入脚本模板
	private String createScript(String script){
		return "|set url| ${URL}"+script+"|\r\n|check|run test with get|${xxxxx}|\r\n";
	}
	/**
	 * 两个参数的构造方法
	 * @param String method
	 * @param String description
	 */
	public CaseModule(){		
		//实例化接口信息列表
		apiList = new ArrayList<>();		
	}
	/**
	 * 带一个参数的构造方法，输入一个文件路径，读取需要创建用例的接口信息
	 * @param inputPath
	 */
	public CaseModule(String inputPath,String outPath){
		//实例化接口信息列表
		apiList = new ArrayList<>();
		parseParamFile(inputPath);
		generateTests(outPath);
		
	}
	
	/**
	 * 用例生成器实现 
	 */
	public boolean generateTests(String outPath){
		if (!apiList.isEmpty()) {
			int count = 1;
			for (ApiClass api : apiList) {
				if(count<10){
					writeToFile(outPath + "/"+0+count + api.getApiName()
							+ "/content.txt", this.paramLoop(api));
					writeToFile(outPath + "/"+0+count + api.getApiName()
							+ "/properties.xml",
							this.caseTemplate(api.getApiTitle()));
				}else{
					writeToFile(outPath + "/"+ count +api.getApiName() + "/content.txt",
							this.paramLoop(api));
					writeToFile(outPath + "/"+ count +api.getApiName() + "/properties.xml",
							this.caseTemplate(api.getApiTitle()));
				}
				count++;
			}
			System.out.println((count-1) + " cases created.\r\nThe output path is: " + new File(outPath).getAbsolutePath());
			this.setFlag(true);
		} else {
			System.out.println("No test cases detected. Nothing to do.");
			this.setFlag(false);
		}
		return this.isFlag();
	}
	
	/**
	 * 解析接口定义文档，完成接口信息分类保存。接口信息保存方法见ApiClass
	 * @param filePath
	 * @see ApiClass
	 */
	public void parseParamFile(String filePath){
		ApiClass anApi = null;
		File file = new File(filePath);
		/**
		 * 文件不存在，直接退出
		 */
		if(!file.exists() || !file.isFile()){
			System.out.println("The specified file is not exist, return...");
			return;
		}
		
		BufferedReader br = null;
		try {
		    br = new BufferedReader(new InputStreamReader(new FileInputStream(file),"UTF-8"));
			String tempString = null;
			/**
			 * 读取接口定义文件时，清空apiList，确保接口用例的范围在一个文件中描述
			 */
			apiList.clear();
			int line = 1;
			while((tempString = br.readLine()) != null) {
				String tempArray[]=tempString.split("\t");
				//先找到起始行，如果找到可以初始化行号 为1，和一些类初始化准备工作,实例化ApiClass保存新接口，将新接口信息添加到apiList中，跳至下一轮循环
				if(tempArray.length == 2 && isNumericTitle(tempArray[0].trim())){
					line =2;
					anApi = new ApiClass();
					anApi.setApiTitle(tempString);
					apiList.add(anApi);
				}else if(anApi!=null && line ==2){
					line +=1;
					anApi.setApiName(tempString.substring(tempString.indexOf("：")+1));
				}else if(anApi!=null && line==3){
					line +=1;
					anApi.setApiDescription(tempString.substring(tempString.indexOf('：')+1));
				}else if(isNumeric(tempArray[0]) && tempArray.length>5 && anApi!=null){
				/**
				*  文件中特定的列，获取参数名，参数类型，参数最大长度; 使用paramGroup方法将参数组合并保存起来
				*  System.out.println(tempString+"===>>>"+tempArray[1]+"-> "+tempArray[3]+"-> "+tempArray[4]);
				*/
				anApi.appendApiParamGroup(tempArray[1],this.makeParamGroup(tempArray[1], this.translateParam(tempArray[3]), tempArray[4]));
				}
				
            }
			
			br.close();
		} catch (FileNotFoundException e) {
			System.out.println("FileNotFoundException occurs.");
		} catch (IOException e) {
			System.out.println("IOException occurs.");
		}finally{
			try {
				br.close();
			} catch (IOException e) {
				System.out.println("IOException----read file error.");
			}
		}
	}
	/**
	 * 参数遍历方法，遍历一个接口的参数列表，组合参数制作接口Url
	 */
	public String paramLoop(ApiClass anApi){
		if(null == anApi){
			return null;
		}
		Map<String,List<String>> paramMap = anApi.getApiParamGroup();
		if(null==paramMap || paramMap.isEmpty()){
			System.out.println("No parameters found, check the config. Error occurs in " + this.toString());
			return null;
		}
		StringBuilder sb = new StringBuilder();
		
		sb.append("需求说明：\r\n!define Method {!-"+anApi.getApiName()+"-!}\r\n\r\n"+anApi.getApiDescription()+"\r\n\r\n");
		sb.append("测试用例\r\n|script|\r\n");
		
		Iterator<String> keys = paramMap.keySet().iterator();
		while(keys.hasNext()){
			String key = keys.next();
			StringBuilder tempScript= new StringBuilder();
			Iterator<String> tempKeys = paramMap.keySet().iterator();
			/**
			 * 一个方法内，去除一个参数后的参数全组和，例：参数1，参数2，参数3，参数4 ==>>
			 * 参数2=${参数2}&参数3=${参数3}&参数4=${参数4} 参数1=${参数1}&参数3=${参数3}&参数4=${参数4}
			 * 参数2=${参数2}&参数1=${参数1}&参数4=${参数4} 参数2=${参数2}&参数3=${参数3}&参数1=${参数1}
			 */
			while(tempKeys.hasNext()){
				String tempKey = tempKeys.next();
				if(tempKey.equals(key)){
//					参数列表多于两个，且判断最后一个参数的情况，去掉最后一个参数的情况
					if(tempScript.length()>0&&!tempKeys.hasNext()){
						tempScript.deleteCharAt(tempScript.length()-1);
					}
					continue;
				}
				tempScript.append(transferParam(tempKey));
				if(tempKeys.hasNext()){
					tempScript.append("&");					
				}
			}
			/**
			 * 遍历一个参数的组合paramList,并添加到测试脚本testScripts	 
			 */
			String scriptTemp= tempScript.toString();
			ArrayList<String> tempList = (ArrayList<String>) paramMap.get(key);
			for(String s:tempList){
				if("".equals(s)||"".equals(scriptTemp)){
					sb.append(this.createScript(scriptTemp+s));
				}else{
					sb.append(this.createScript(scriptTemp +"&"+s));
				}
			}
			
		}
		return sb.toString();
	}
	
	/**
	 * 参数组合方法，为每个参数建立组合数组，留待写测试脚本用。传入三个参数：参数名，参数类型，参数最大长度
	 * @param name
	 * @param type
	 * @param maxLength
	 * @return List<String> paramGroup
	 */
	public List<String> makeParamGroup(String name,String type,String maxLength){
		ArrayList<String> paramList=new ArrayList<>();   //实例化数组表
		paramList.add("");
		paramList.add(name+"=");
		if("Method".equals(name)){
			paramList.add(name+"=${"+name+"}");
		}
		if(isNumeric(maxLength)){
			paramList.add(name+"=${"+this.translateParam(type)+(Integer.parseInt(maxLength)+1)+"}");
		}
		return paramList;		
	}
//判断一个字符串是一个数字
	private boolean isNumeric(String str) {
		Pattern pattern = Pattern.compile("[0-9]*");
		Matcher isNum = pattern.matcher(str);
		if (!isNum.matches()) {
			return false;
		}
		return true;
	}
//判断一个字符串是这样的形式 ： 【数字】.【数字】 例： 4.36
	public boolean isNumericTitle(String str) {
		Pattern pattern = Pattern.compile(".*[0-9].*");
		Matcher isNum = pattern.matcher(str);
		if (!isNum.matches()) {
			return false;
		}
		return true;
	}
	/**
	 * 将一个字符串转换成如下形式：    string -> string=${string}
	 * @param name
	 * @return
	 */
	private String transferParam(String name){
		return "!-"+name+"-!" + "=${"+name+"}";
	}
	/**
	 * 将特定字符串翻译成英文
	 * @param name
	 * @return
	 */
	private String translateParam(String name){
		if("字符串".equals(name)){
			return "String";
		}
		if("整数".equals(name)||"数字".equals(name)){
			return "Int";
		}
		return name;
	}
	
	/**
	 * 写文件, 处理好的测试脚本写入文件
	 */
	public void writeToFile(String filePath, String content){
		File file = new File(filePath);
		/**
		 * 文件不存在，新建文件
		 */
		if(!file.exists() || !file.isFile()){
			try {
				if(!file.getParentFile().exists()){
					file.getParentFile().mkdirs();
				}
				if(file.createNewFile()){
					System.out.println("file created");
				}
			} catch (IOException e) {
				System.out.println("File created failed, since unknown IOException");
			}
		}
		BufferedWriter bw = null;
		try {
			bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file),"UTF-8"));
			bw.write(content);
			bw.close();
		} catch (UnsupportedEncodingException | FileNotFoundException e) {
			System.out.println("UnsupportedEncodingException | FileNotFoundException occurs...");
		} catch (IOException e) {
			System.out.println("IOException occurs...");
		}finally{
			try {
				bw.close();
			} catch (IOException e) {
				System.out.println("IOException----write file error.");
			}
		}
	}
	
	/**
	 * 字符串用例属性内容模板
	 */
	private StringBuilder sb = new StringBuilder();
	/**
	 * 更改内容模板,增加‘帮助’字段--help，将生成内容以字符串作为返回值
	 * @param help
	 * @return
	 */
	public String caseTemplate(String help){
		if(sb.length()>0){
			sb.delete(0, sb.length());
//			System.out.println("测试StringBuilder的长度: "+ sb.length());
		}
		sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n");
		sb.append("<properties>\r\n");
		sb.append("<Edit/>\r\n<Help>"+ help +"</Help>\r\n<LastModifyingUser>zczx</LastModifyingUser>\r\n<Properties/>\r\n<Test/>\r\n<Versions/>\r\n<secure-write/>\r\n</properties>\r\n");
		return sb.toString();
	}
}
